/*****************************************************
** Author:
** Date: 2019-07-25
*
*****************************************************/
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "ps2.h"

#define log_dbg(_fmt_, ...)   \
            printf("[PS2][%s:%d][%s][DBG] "_fmt_"\r\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__)
#define log_inf(_fmt_, ...)   \
            printf("[PS2][%s:%d][%s][INF] "_fmt_"\r\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__)
#define log_wrn(_fmt_, ...)   \
            printf("[PS2][%s:%d][%s][WRN] "_fmt_"\r\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__)
#define log_err(_fmt_, ...)   \
            printf("[PS2][%s:%d][%s][ERR] "_fmt_"\r\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__)


typedef struct {
    Ps2CbStru cb;
} Ps2ObjStru;

#define RECV_DATA_SIZE_MAX  9

#define PS2_JOYSTICK_HIGH_THRESHOLD     0x80
#define PS2_JOYSTICK_LOW_THRESHOLD      0x7f

/*
 * 进入配置
 */
static unsigned char enter_config[]    = {0x01,0x43,0x00,0x01,0x00,0x00,0x00,0x00,0x00};
/*
 * 发送模式设置
 * argv[3]: 软件设置发送模式
 *      analog  = 0x01
 *      digital = 0x00
 *
 * argv[4]:
 *      0x03 锁存设置，即不可通过按键“MODE ”设置模式。
 *      0xEE 不锁存软件设置，可通过按键“MODE ”设置模式。
 */
static const unsigned char set_mode[]        = {0x01,0x44,0x00,0x01,0x03,0x00,0x00,0x00,0x00};
static const unsigned char set_bytes_large[] = {0x01,0x4F,0x00,0xFF,0xFF,0x03,0x00,0x00,0x00};
/*
 * 完成并保存配置
 */
static const unsigned char exit_config[]     = {0x01,0x43,0x00,0x00,0x5A,0x5A,0x5A,0x5A,0x5A};
/*
 * 震动设置
 */
static const unsigned char enable_rumble[]   = {0x01,0x4D,0x00,0x00,0x01};
static const unsigned char type_read[]       = {0x01,0x45,0x00,0x5A,0x5A,0x5A,0x5A,0x5A,0x5A};

static const unsigned char read_status[] = {0x01, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

static void *(*Ps2Malloc)(unsigned int size);
static void (*Ps2Free)(void *ptr);

/**
 * @brief
 * @param  None
 * @retval None
 */
void Ps2RegisterMalloc(void *(*malloc)(unsigned int size), void (*free)(void *ptr))
{
    if (malloc == NULL || free == NULL) {
        return;
    }

    Ps2Malloc = malloc;
    Ps2Free = free;
}

Ps2ObjHdl Ps2Init(Ps2CbStru *cfg)
{
    Ps2ObjStru *ctx;
    unsigned char rx[RECV_DATA_SIZE_MAX] = {0};

    ctx = (Ps2ObjStru *)Ps2Malloc(sizeof(Ps2ObjStru));
    if (ctx == NULL) {
        return NULL;
    }

    memcpy(&ctx->cb, cfg, sizeof(Ps2CbStru));

    if (ctx->cb.selChipCb == NULL ||
        ctx->cb.spiXferCb == NULL ||
        ctx->cb.delayMsCb == NULL) {
        Ps2Free(ctx);
        return NULL;
    }

    ctx->cb.selChipCb(false);
    ctx->cb.spiXferCb((unsigned char *)enter_config, rx, sizeof(enter_config));
    ctx->cb.selChipCb(true);

    ctx->cb.delayMsCb(100);

    ctx->cb.selChipCb(false);
    ctx->cb.spiXferCb((unsigned char *)set_mode, rx, sizeof(set_mode));
    ctx->cb.selChipCb(true);

    ctx->cb.delayMsCb(100);

    ctx->cb.selChipCb(false);
    ctx->cb.spiXferCb((unsigned char *)set_bytes_large, rx, sizeof(set_bytes_large));
    ctx->cb.selChipCb(true);

    ctx->cb.delayMsCb(100);

    ctx->cb.selChipCb(false);
    ctx->cb.spiXferCb((unsigned char *)exit_config, rx, sizeof(exit_config));
    ctx->cb.selChipCb(true);

    ctx->cb.delayMsCb(100);

    return (Ps2ObjHdl)ctx;
}

void Ps2Deinit(Ps2ObjHdl hdl)
{
    Ps2ObjStru *ctx = (Ps2ObjStru *)hdl;
    Ps2Free(ctx);
}

int Ps2GetState(Ps2ObjHdl hdl,
                unsigned char key[PS2_KEY_MAX], int *keyLen,
                unsigned char joystick[PS2_JOYSTICK_MAX])
{
    Ps2ObjStru *ctx = (Ps2ObjStru *)hdl;
    unsigned char rx[RECV_DATA_SIZE_MAX] = {0};
    unsigned short keyVal;
    int joystick_left_x;
    int joystick_left_y;
    int joystick_right_x;
    int joystick_right_y;
    int cnt, i;

    if (ctx->cb.selChipCb == NULL ||
        ctx->cb.spiXferCb == NULL) {
        return -1;;
    }

    ctx->cb.selChipCb(false);
    ctx->cb.spiXferCb((unsigned char *)read_status, rx, sizeof(read_status));
    ctx->cb.selChipCb(true);

    // log_dbg("rx[0] %#x rx[1] %#x rx[2] %#x", rx[0], rx[1], rx[2]);

    if (rx[2] != 0x5a) {
        return -1;
    }

    keyVal = *(unsigned short *)(&rx[3]);

    memset(key, 0x0, sizeof(key));

    for (i = 0, cnt = 0; i < PS2_KEY_MAX; ++i) {
        if (keyVal & (1<<i)) {
            continue;
        }
        key[cnt++] = i;
    }

    joystick_left_x  = rx[8];
    joystick_left_y  = rx[7];
    joystick_right_x = rx[6];
    joystick_right_y = rx[5];

    if (rx[5] == 0xff && rx[6] == 0xff && rx[7] == 0xff && rx[8] == 0xff) {
        return -2;
    }

    memset(joystick, 0x0, PS2_JOYSTICK_MAX);

    if (joystick_left_x > PS2_JOYSTICK_HIGH_THRESHOLD) {
        joystick[PS2_JOYSTICK_LEFT_DOWN] = joystick_left_x - 0x80;
    } else if (joystick_left_x < PS2_JOYSTICK_LOW_THRESHOLD) {
        joystick[PS2_JOYSTICK_LEFT_UP] = 0x80 - joystick_left_x;
    }

    if (joystick_left_y > PS2_JOYSTICK_HIGH_THRESHOLD) {
        joystick[PS2_JOYSTICK_LEFT_RIGHT] = joystick_left_y - 0x80;
    } else if (joystick_left_y < PS2_JOYSTICK_LOW_THRESHOLD) {
        joystick[PS2_JOYSTICK_LEFT_LEFT] = 0x80 - joystick_left_y;
    }

    if (joystick_right_x > PS2_JOYSTICK_HIGH_THRESHOLD) {
        joystick[PS2_JOYSTICK_RIGHT_DOWN] = joystick_right_x - 0x80;
    } else if (joystick_right_x < PS2_JOYSTICK_LOW_THRESHOLD) {
        joystick[PS2_JOYSTICK_RIGHT_UP] = 0x80 - joystick_right_x;
    }

    if (joystick_right_y > PS2_JOYSTICK_HIGH_THRESHOLD) {
        joystick[PS2_JOYSTICK_RIGHT_RIGHT] = joystick_right_y - 0x80;
    } else if (joystick_right_y < PS2_JOYSTICK_LOW_THRESHOLD) {
        joystick[PS2_JOYSTICK_RIGHT_LEFT] = 0x80 - joystick_right_y;
    }

    *keyLen = cnt;
    return 0;
}

int Ps2GetAllState(Ps2ObjHdl hdl, unsigned char key[PS2_KEY_MAX],
                unsigned char joystick[PS2_JOYSTICK_MAX])
{
    Ps2ObjStru *ctx = (Ps2ObjStru *)hdl;
    unsigned char rx[RECV_DATA_SIZE_MAX] = {0};
    unsigned short keyVal;
    int joystick_left_x;
    int joystick_left_y;
    int joystick_right_x;
    int joystick_right_y;
    int i;

    if (ctx->cb.selChipCb == NULL ||
        ctx->cb.spiXferCb == NULL) {
        return -1;
    }

    ctx->cb.selChipCb(false);
    ctx->cb.spiXferCb((unsigned char *)read_status, rx, sizeof(read_status));
    ctx->cb.selChipCb(true);

    // log_dbg("rx[0] %#x rx[1] %#x rx[2] %#x", rx[0], rx[1], rx[2]);

    if (rx[2] != 0x5a) {
        return -1;
    }

    keyVal = *(unsigned short *)(&rx[3]);
    log_dbg("keyVal %#x", keyVal);

    memset(key, 0x0, sizeof(key));

    for (i = 0; i < PS2_KEY_MAX; ++i) {
        if (keyVal & (1<<i)) {
            key[i] = 0;
        } else {
            key[i] = 1;

        }
    }

    joystick_left_x  = rx[8];
    joystick_left_y  = rx[7];
    joystick_right_x = rx[6];
    joystick_right_y = rx[5];

    if (rx[5] == 0xff && rx[6] == 0xff && rx[7] == 0xff && rx[8] == 0xff) {
        return -2;
    }

    memset(joystick, 0x0, PS2_JOYSTICK_MAX);

    if (joystick_left_x > PS2_JOYSTICK_HIGH_THRESHOLD) {
        joystick[PS2_JOYSTICK_LEFT_DOWN] = joystick_left_x - 0x80;
    } else if (joystick_left_x < PS2_JOYSTICK_LOW_THRESHOLD) {
        joystick[PS2_JOYSTICK_LEFT_UP] = 0x80 - joystick_left_x;
    }

    if (joystick_left_y > PS2_JOYSTICK_HIGH_THRESHOLD) {
        joystick[PS2_JOYSTICK_LEFT_RIGHT] = joystick_left_y - 0x80;
    } else if (joystick_left_y < PS2_JOYSTICK_LOW_THRESHOLD) {
        joystick[PS2_JOYSTICK_LEFT_LEFT] = 0x80 - joystick_left_y;
    }

    if (joystick_right_x > PS2_JOYSTICK_HIGH_THRESHOLD) {
        joystick[PS2_JOYSTICK_RIGHT_DOWN] = joystick_right_x - 0x80;
    } else if (joystick_right_x < PS2_JOYSTICK_LOW_THRESHOLD) {
        joystick[PS2_JOYSTICK_RIGHT_UP] = 0x80 - joystick_right_x;
    }

    if (joystick_right_y > PS2_JOYSTICK_HIGH_THRESHOLD) {
        joystick[PS2_JOYSTICK_RIGHT_RIGHT] = joystick_right_y - 0x80;
    } else if (joystick_right_y < PS2_JOYSTICK_LOW_THRESHOLD) {
        joystick[PS2_JOYSTICK_RIGHT_LEFT] = 0x80 - joystick_right_y;
    }

    return 0;
}

void Ps2ShowKey(unsigned char key)
{
    char *keyStr[] = {
        "SELECT",
        "L3",
        "R3",
        "START",
        "UP",
        "RIGHT",
        "DOWN",
        "LEFT",
        "L2",
        "R2",
        "L1",
        "R1",
        "TRIANGLE",
        "ROUNDNES",
        "X",
        "SQUARE",
    };
    log_inf("key[%d] %s", key, keyStr[key]);
}

void Ps2ShowJoystick(unsigned char joystick, unsigned char value)
{
    char *joyStr[] = {
        "LEFT_UP",
        "LEFT_DOWN",
        "LEFT_LEFT",
        "LEFT_RIGHT",
        "RIGHT_UP",
        "RIGHT_DOWN",
        "RIGHT_LEFT",
        "RIGHT_RIGHT",
    };
    if (value) {
        log_inf("joystick %s %d", joyStr[joystick], value);
    }
}

void show(unsigned char *cmd)
{
    int i;
    char *msg;
    unsigned short key = *(unsigned short *)(&cmd[3]);

    if (cmd[2] != 0x5a) {
        return;
    }

    // log_dbg("get key %#x", key);
    for (i = 0; i < 16; ++i) {
        if (key & (1<<i)) {
            continue;
        }

        switch (i) {
            case PS2_KEY_SELECT:
                msg = "SELECT";
                break;
            case PS2_KEY_L3:
                msg = "L3";
                break;
            case PS2_KEY_R3:
                msg = "R3";
                break;
            case PS2_KEY_START:
                msg = "START";
                break;
            case PS2_KEY_UP:
                msg = "UP";
                break;
            case PS2_KEY_RIGHT:
                msg = "RIGHT";
                break;
            case PS2_KEY_DOWN:
                msg = "DOWN";
                break;
            case PS2_KEY_LEFT:
                msg = "LEFT";
                break;
            case PS2_KEY_L2:
                msg = "L2";
                break;
            case PS2_KEY_R2:
                msg = "R2";
                break;
            case PS2_KEY_L1:
                msg = "L1";
                break;
            case PS2_KEY_R1:
                msg = "R1";
                break;
            case PS2_KEY_TRIANGLE:
                msg = "TRIANGLE";
                break;
            case PS2_KEY_ROUNDNES:
                msg = "ROUNDNES";
                break;
            case PS2_KEY_X:
                msg = "X";
                break;
            case PS2_KEY_SQUARE:
                msg = "SQUARE";
                break;
            default:
                return;
        }
        log_dbg("get key %s", msg);
    }

    int joystick_left_x = cmd[8];
    int joystick_left_y = cmd[7];
    int joystick_right_x = cmd[6];
    int joystick_right_y = cmd[5];

    if (joystick_left_x > PS2_JOYSTICK_HIGH_THRESHOLD) {
        log_dbg("[LEFT] down %d", joystick_left_x - 0x80);
    } else if (joystick_left_x < PS2_JOYSTICK_LOW_THRESHOLD) {
        log_dbg("[LEFT] up %d", 0x80 - joystick_left_x);
    }

    if (joystick_left_y > PS2_JOYSTICK_HIGH_THRESHOLD) {
        log_dbg("[LEFT] right %d", joystick_left_y - 0x80);
    } else if (joystick_left_y < PS2_JOYSTICK_LOW_THRESHOLD) {
        log_dbg("[LEFT] left %d", 0x80 - joystick_left_y);
    }

    if (joystick_right_x > PS2_JOYSTICK_HIGH_THRESHOLD) {
        log_dbg("[RIGHT] down %d", joystick_right_x - 0x80);
    } else if (joystick_right_x < PS2_JOYSTICK_LOW_THRESHOLD) {
        log_dbg("[RIGHT] up %d", 0x80 - joystick_right_x);
    }

    if (joystick_right_y > PS2_JOYSTICK_HIGH_THRESHOLD) {
        log_dbg("[RIGHT] right %d", joystick_right_y - 0x80);
    } else if (joystick_right_y < PS2_JOYSTICK_LOW_THRESHOLD) {
        log_dbg("[RIGHT] left %d", 0x80 - joystick_right_y);
    }
}



/* End of file */
